#include "StdAfx.h"
#include "DisplayDevice.h"

using namespace Adaptive::Foundation;
using namespace Adaptive::Win32;

/*--------------------------------------------------------------------------------
 * Constructor / Dispose / Destructor Methods
 *------------------------------------------------------------------------------*/
#pragma region Constructor / Dispose / Destructor Methods
// <summary>
// Initializes a new instance of the class.
// </summary>
// <remarks>
// This is the default constructor.
// </remarks>
DisplayDevice::DisplayDevice() : AiObjectBase()
{
	_deviceName = String::Empty;
	_deviceId = String::Empty;
	_deviceString = String::Empty;
	_deviceKey = String::Empty;
	_stateFlags = 0;
	_driverVersion = 0;
	_technology = 0;
	_horizontalSize = 0;
	_verticalSize = 0;
	_horizontalResolution = 0;
	_verticalResolution = 0;
	_logicalPixelsX = 0;
	_logicalPixelsY = 0;
	_bitsPerPixel = 0;
	_planeCount = 0;
	_brushCount = 0;
	_penCount = 0;
	_fontCount = 0;
	_colorCount = 0;
	_aspectX = 0;
	_aspectY = 0;
	_aspectXY = 0;
	_verticalRefreshRate = 0;

}
// <summary>
// Initializes a new instance of the class for the specified parameters.
// </summary>
// <param name="deviceName">The unique name associated with the device.</param>
// <param name="deviceId">The unique system identifier for the device.</param>
// <param name="displayName">The display name for the device.</param>
// <param name="deviceKey">The registry key for the device.</param>
// <param name="flags">The state flags associated with the device.</param>
DisplayDevice::DisplayDevice(String^ deviceName, String^ deviceId, String^ displayName, String ^deviceKey, int flags) : AiObjectBase()
{
	_deviceName = String::Empty;
	_deviceId = String::Empty;
	_deviceString = String::Empty;
	_deviceKey = String::Empty;
	_stateFlags = 0;
	_driverVersion = 0;
	_technology = 0;
	_horizontalSize = 0;
	_verticalSize = 0;
	_horizontalResolution = 0;
	_verticalResolution = 0;
	_logicalPixelsX = 0;
	_logicalPixelsY = 0;
	_bitsPerPixel = 0;
	_planeCount = 0;
	_brushCount = 0;
	_penCount = 0;
	_fontCount = 0;
	_colorCount = 0;
	_aspectX = 0;
	_aspectY = 0;
	_aspectXY = 0;
	_verticalRefreshRate = 0;

	_deviceName = deviceName;
	_deviceId = deviceId;
	_deviceString = displayName;
	_deviceKey = deviceKey;
	_stateFlags = flags;

	LoadInternalInformation();
}
// <summary>
// Deallocates internally allocated objects.
// </summary>
DisplayDevice::~DisplayDevice()
{
	if (!Disposed)
	{
		_deviceName = nullptr;
		_deviceId = nullptr;
		_deviceString = nullptr;
		_deviceKey = nullptr;
	}
}
#pragma endregion

/*--------------------------------------------------------------------------------
 * Public Properties
 *------------------------------------------------------------------------------*/
#pragma region Public Properties
// <summary>
// Gets a value indicating whether the display device is attached to the desktop.
// </summary>
// <value>
// <b>true</b> if the device is part of the desktop; otherwise, <b>false</b>.
// </value>
bool DisplayDevice::AttachedToDesktop::get()
{
	return (_stateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP);
}
// <summary>
// Gets a value indicating whether the display device is a mirroring device.
// </summary>
// <remarks>
// Represents a pseudo device used to mirror application drawing for remoting or other purposes. An invisible pseudo 
// monitor is associated with this device. For example, NetMeeting uses it. Note that GetSystemMetrics(SM_MONITORS) 
// only accounts for visible display monitors.
// </remarks>
// <value>
// <b>true</b> if the device is psuedo device for mirroring; otherwise, <b>false</b>.
// </value>
bool DisplayDevice::MirroringDriver::get()
{
	return ((_stateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) != 0);
}
// <summary>
// Gets a value indicating whether the display device has more display modes than its output devices support.
// </summary>
// <value>
// <b>true</b> if the device has more display modes than its output devices support; otherwise, <b>false</b>.
// </value>
bool DisplayDevice::ModesPruned::get()
{
	return ((_stateFlags & DISPLAY_DEVICE_MODESPRUNED) != 0);
}
// <summary>
// Gets a value indicating whether the display device is removable.
// </summary>
// <value>
// <b>true</b> if the device is removable; otherwise, <b>false</b>.
// </value>
bool DisplayDevice::Removable::get()
{
	return ((_stateFlags & DISPLAY_DEVICE_REMOVABLE) != 0);
}
// <summary>
// Gets a value indicating whether the display device is VGA compatible.
// </summary>
// <value>
// <b>true</b> if the device is VGA compatible; otherwise, <b>false</b>.
// </value>
bool DisplayDevice::VgaCompatible::get()
{
	return ((_stateFlags & DISPLAY_DEVICE_VGA_COMPATIBLE) != 0);
}
// <summary>
// Gets a value indicating whether the display device is the primary display device.
// </summary>
// <value>
// <b>true</b> if the device is the primary display device; otherwise, <b>false</b>.
// </value>
bool DisplayDevice::Primary::get()
{
	return ((_stateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) != 0);
}
// <summary>
// Gets the name of the device.
// </summary>
// <value>
// The name of the the adapter device or the monitor device.
// </value>
String^ DisplayDevice::DeviceName::get()
{
	return _deviceName;
}
// <summary>
// Gets the unique identifier value of the device.
// </summary>
// <value>
// This is the plug-and-play ID value for the device.
// </value>
String^ DisplayDevice::DeviceID::get()
{
	return _deviceId;
}
// <summary>
// Gets the device context string.
// </summary>
// <value>
// A description of the display adapter or of the display monitor.
// </value>
String^ DisplayDevice::DeviceString::get()
{
	return _deviceString;
}
// <summary>
// Gets the device registry key.
// </summary>
// <value>
// May be reserved, or a registry key associated with the device.
// </value>
String^ DisplayDevice::DeviceKey::get()
{
	return _deviceKey;
}
// <summary>
// Gets the device driver version.
// </summary>
// <value>
// A device driver version as an integer.
// </value>
int DisplayDevice::DriverVersion::get()
{
	return _driverVersion;
}
// <summary>
// Gets the device technology value.
// </summary>
// <value>
// A device technology constant value.
// </value>
int DisplayDevice::Technology::get()
{
	return _technology;
}
// <summary>
// Gets the width of the physical screen.
// </summary>
// <value>
// The width of the screen, in millimeters.
// </value>
int DisplayDevice::HorizontalSize::get()
{
	return _horizontalSize;
}
// <summary>
// Gets the height of the physical screen.
// </summary>
// <value>
// The height of the screen, in millimeters.
// </value>
int DisplayDevice::VerticalSize::get()
{
	return _verticalSize;
}
// <summary>
// Gets the width of the screen in pixels.
// </summary>
// <value>
// The width of the screen in pixels.
// </value>
int DisplayDevice::HorizontalResolution::get()
{
	return _horizontalResolution;
}
// <summary>
// Gets the height of the screen in pixels.
// </summary>
// <value>
// The height of the screen in pixels.
// </value>
int DisplayDevice::VerticalResolution::get()
{
	return _verticalResolution;
}
// <summary>
// Gets the number of pixels per logical inch along the screen width.
// </summary>
// <remarks>
// In a system with multiple display monitors, this value is the same for all monitors.
// </remarks>
// <value>
// The number of pixels per inch along the width of the screen.
// </value>
int DisplayDevice::LogicalPixelsX::get()
{
	return _logicalPixelsX;
}
// <summary>
// Gets the number of pixels per logical inch along the screen height.
// </summary>
// <remarks>
// In a system with multiple display monitors, this value is the same for all monitors.
// </remarks>
// <value>
// The number of pixels per inch along the height of the screen.
// </value>
int DisplayDevice::LogicalPixelsY::get()
{
	return _logicalPixelsY;
}
// <summary>
// Gets the number of adjacent color bits for each pixel
// </summary>
// <value>
// The number of bits used to define each pixel.  This determines color-depth.
// </value>
int DisplayDevice::BitsPerPixel::get()
{
	return _bitsPerPixel;
}
// <summary>
// Gets the number of color planes.
// </summary>
// <value>
// The number of color planes.
// </value>
int DisplayDevice::PlaneCount::get()
{
	return _planeCount;
}
// <summary>
// Gets the number of entries in the device's color table.
// </summary>
// <remarks>
// This value is valid only if the device has a color depth of no more than 8 bits per pixel. 
// For devices with greater color depths,  1 is returned.
// </remarks>
// <value>
// The number of entries in the color table or 1.
// </value>
int DisplayDevice::ColorCount::get()
{
	return _colorCount;
}
// <summary>
// Gets the number of device-specific fonts.
// </summary>
// <value>
// The number of device-specific fonts.
// </value>
int DisplayDevice::FontCount::get()
{
	return _fontCount;
}
// <summary>
// Gets the number of device-specific pens.
// </summary>
// <value>
// The number of device-specific pens.
// </value>
int DisplayDevice::PenCount::get()
{
	return _penCount;
}
// <summary>
// Gets the number of device-specific brushes.
// </summary>
// <value>
// The number of device-specific brushes.
// </value>
int DisplayDevice::BrushCount::get()
{
	return _brushCount;
}
// <summary>
// Gets the relative width of a device pixel used for line drawing.
// </summary>
// <value>
// The relative width of a pixel alogn the X co-ordinate.
// </value>
int DisplayDevice::AspectX::get()
{
	return _aspectX;
}
// <summary>
// Gets the relative height of a device pixel used for line drawing.
// </summary>
// <value>
// The relative height of a pixel alogn the Y co-ordinate.
// </value>
int DisplayDevice::AspectY::get()
{
	return _aspectY;
}
// <summary>
// Gets the diagonal width of the device pixel used for line drawing.
// </summary>
// <value>
// The size of the pixel's diagonal diameter.
// </value>
int DisplayDevice::AspectXY::get()
{
	return _aspectXY;
}
// <summary>
// Gets the current vertical refresh rate of the device, in cycles per second (Hz). 
// </summary>
// <remarks>
// A vertical refresh rate value of 0 or 1 represents the display hardware's default refresh rate. 
// This default rate is typically set by switches on a display card or computer motherboard, or by 
// a configuration program that does not use display functions such as <b>ChangeDisplaySettings</b>.
// </remarks>
// <value>
// The vertical refresh rate in cycles per second (Hz), or a 0 or 1 value.
// </value>
int DisplayDevice::VerticalRefreshRate::get()
{
	return _verticalRefreshRate;
}
#pragma endregion

/*--------------------------------------------------------------------------------
 * Public Methods / Functions
 *------------------------------------------------------------------------------*/
#pragma region Public Methods / Functions
// <summary>
//
// </summary>
// <returns>
//
// </returns>
array<DisplayDevice^>^ DisplayDevice::GetDisplayDeviceList()
{
	BOOL result = FALSE;
	DWORD deviceNumber = 0;
	DISPLAY_DEVICE* buffer = NULL;
	int itemCount = 0;
	array<DisplayDevice^>^ returnValue = nullptr;
	DisplayDevice^ newItem = nullptr;

	//Count devices.
	do
	{
		buffer = new DISPLAY_DEVICE();
		buffer->cb = sizeof(DISPLAY_DEVICE);
		result = EnumDisplayDevices(NULL, deviceNumber, buffer, 0);
		deviceNumber++;
		delete(buffer);
	} while (result);

	itemCount = deviceNumber;
	deviceNumber = 0;
	returnValue  = gcnew array<DisplayDevice^>(itemCount);

	do
	{
		buffer = new DISPLAY_DEVICE();
		buffer->cb = sizeof(DISPLAY_DEVICE);
		result = EnumDisplayDevices(NULL, deviceNumber, buffer, 0);
	
		newItem = gcnew DisplayDevice(gcnew String(buffer->DeviceName), 
									  gcnew String(buffer->DeviceID), 
									  gcnew String(buffer->DeviceString),
									  gcnew String(buffer->DeviceKey), (int)buffer->StateFlags);
		returnValue[(int)deviceNumber] = newItem;

		delete(buffer);
		deviceNumber++;
	} while (result);

	return returnValue;
}
// <summary>
// Modifies the screen resolution to the desired widht and height.
// </summary>
// <param name="width">The desired width of the display device, in pixels.</param>
// <param name="height">The desired height of the display device, in pixels.</param>
// <returns>
// <b>true</b> if the operation was completed successfully; otherwise, <b>false</b>.
// </returns>
bool DisplayDevice::ChangeScreenResolution(int width, int height)
{
	DEVMODE* mode = NULL;
	LONG	result = 0;
	bool	returnValue = false;
	CString	devName = _deviceName;
	DWORD	flags = 0;

	//Create the device mode instructions.
	mode = new DEVMODE();
	mode->dmPelsWidth = width;
	mode->dmPelsHeight = height;
	mode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
	mode->dmSize = sizeof(DEVMODE);

	//Create the flags.
	flags = CDS_SET_PRIMARY | CDS_TEST;

	result = ChangeDisplaySettingsEx(devName, mode, NULL, flags, NULL);
	if (result == DISP_CHANGE_SUCCESSFUL)
	{
		flags = CDS_SET_PRIMARY;

		result = ChangeDisplaySettingsEx(devName, mode, NULL, flags, NULL);
		if (result == DISP_CHANGE_SUCCESSFUL)
			returnValue = true;
	}
	delete(mode);
	return returnValue;
}
// <summary>
// Modifies the screen resolution to the desired widht and height.
// </summary>
// <param name="width">The desired width of the display device, in pixels.</param>
// <param name="height">The desired height of the display device, in pixels.</param>
// <param name="colorDepth">A <see cref="ColorDepth"/> enumerated value indicating the desired color depth.</param>
// <returns>
// <b>true</b> if the operation was completed successfully; otherwise, <b>false</b>.
// </returns>
bool DisplayDevice::ChangeScreenResolutionAndColorDepth(int width, int height, ColorDepth colorDepth)
{
	DEVMODE* mode = NULL;
	LONG	result = 0;
	bool	returnValue = false;
	CString	devName = _deviceName;
	DWORD	flags = 0;

	//Create the device mode instructions.
	mode = new DEVMODE();
	mode->dmPelsWidth = width;
	mode->dmPelsHeight = height;
	mode->dmBitsPerPel = (DWORD)colorDepth;
	mode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
	mode->dmSize = sizeof(DEVMODE);
	

	//Create the flags.
	flags = CDS_SET_PRIMARY | CDS_TEST;
	result = ChangeDisplaySettingsEx(devName, mode, NULL, flags, NULL);
	if (result == DISP_CHANGE_SUCCESSFUL)
	{
		flags = CDS_SET_PRIMARY;
		result = ChangeDisplaySettingsEx(devName, mode, NULL, flags, NULL);
		if (result == DISP_CHANGE_SUCCESSFUL)
			returnValue = true;
	}
	delete(mode);
	return returnValue;
}
// <summary>
// Changes the display mode to full screen.
// </summary>
// <returns>
// <b>true</b> if the operation was completed successfully; otherwise, <b>false</b>.
// </returns>
bool DisplayDevice::GotoFullScreen()
{
	DEVMODE* mode = NULL;
	LONG	result = 0;
	bool	returnValue = false;
	CString	devName = _deviceName;
	DWORD	flags = 0;

	//Create the device mode instructions.
	mode = new DEVMODE();
	mode->dmDisplayFlags = CDS_FULLSCREEN;
	mode->dmFields = DM_DISPLAYFLAGS;
	mode->dmSize = sizeof(DEVMODE);
	
	//Create the flags.
	flags = CDS_SET_PRIMARY | CDS_TEST;

	result = ChangeDisplaySettingsEx(devName, mode, NULL, flags, NULL);
	if (result == DISP_CHANGE_SUCCESSFUL)
	{
		flags = CDS_SET_PRIMARY;
		result = ChangeDisplaySettingsEx(devName, mode, NULL, flags, NULL);
		if (result == DISP_CHANGE_SUCCESSFUL)
			returnValue = true;
	}
	delete(mode);
	return returnValue;

}
// <summary>
// Changes the display mode to normal windowed mode.
// </summary>
// <returns>
// <b>true</b> if the operation was completed successfully; otherwise, <b>false</b>.
// </returns>
bool DisplayDevice::GotoWindowedMode()
{
	DEVMODE* mode = NULL;
	LONG	result = 0;
	bool	returnValue = false;
	CString	devName = _deviceName;
	DWORD	flags = 0;

	//Create the device mode instructions.
	mode = new DEVMODE();
	mode->dmDisplayFlags = CDS_RESET;
	mode->dmFields = DM_DISPLAYFLAGS;
	mode->dmSize = sizeof(DEVMODE);
	
	//Create the flags.
	flags = CDS_SET_PRIMARY | CDS_TEST;

	result = ChangeDisplaySettingsEx(devName, mode, NULL, flags, NULL);
	if (result == DISP_CHANGE_SUCCESSFUL)
		returnValue = true;

	delete(mode);
	return returnValue;
}
// <summary>
// Refreshes the information content for the display device instance.
// </summary>
void DisplayDevice::Refresh()
{
	LoadInternalInformation();
}
#pragma endregion

/*--------------------------------------------------------------------------------
 * Private Methods / Functions
 *------------------------------------------------------------------------------*/
#pragma region Private Methods / Functions

void DisplayDevice::LoadInternalInformation()
{
	BOOL result = FALSE;
	DWORD graphicsModeNumber = ENUM_CURRENT_SETTINGS;
	DEVMODE* deviceMode = NULL;
	DWORD flags = 0;
	CString devName = _deviceName;
	CString output = "";
	HDC hDc = NULL;
	CString driver = "DISPLAY";

	deviceMode = new DEVMODE();
	deviceMode->dmSize = sizeof(DEVMODE);

	result = EnumDisplaySettingsEx(devName, graphicsModeNumber, deviceMode, flags);
	if (result)
	{
		hDc = CreateDC(driver, devName, output, deviceMode);
		if (hDc != NULL)
		{
			_driverVersion = GetDeviceCaps(hDc, DRIVERVERSION);
			_technology = GetDeviceCaps(hDc, TECHNOLOGY);
			_horizontalSize = GetDeviceCaps(hDc, HORZSIZE);
			_verticalSize = GetDeviceCaps(hDc, VERTSIZE);
			_horizontalResolution = GetDeviceCaps(hDc, HORZRES);
			_verticalResolution = GetDeviceCaps(hDc, VERTRES);
			_logicalPixelsX = GetDeviceCaps(hDc, LOGPIXELSX);
			_logicalPixelsY = GetDeviceCaps(hDc, LOGPIXELSY);
			_bitsPerPixel = GetDeviceCaps(hDc, BITSPIXEL);
			_planeCount = GetDeviceCaps(hDc, PLANES);
			_brushCount = +GetDeviceCaps(hDc, NUMBRUSHES);
			_penCount = GetDeviceCaps(hDc, NUMPENS);
			_fontCount = GetDeviceCaps(hDc, NUMFONTS);
			_colorCount = GetDeviceCaps(hDc, NUMCOLORS);
			_aspectX = GetDeviceCaps(hDc, ASPECTX);
			_aspectY = GetDeviceCaps(hDc, ASPECTY);
			_aspectXY = GetDeviceCaps(hDc, ASPECTXY);
			_verticalRefreshRate = GetDeviceCaps(hDc, VREFRESH);

			DeleteDC(hDc);
		}
	}

	delete(deviceMode);
	deviceMode = NULL;
	hDc = NULL;

}
#pragma endregion

